<?php
###############   COPYLEFT GPLv3 LICENSE   ###############
##
## JFX Version 0.2.9
## Website Management Software
## www.jfxcms.com
##
## Copyright 2009 GPLv3 - http://www.opensource.org/licenses/gpl-3.0.html
##
## Anthony Gallon
## oi_antz@hotmail.com
##
## Permission is hereby granted to any person having a copy of this software
## to freely use and modify as required so long as the copyright notices
## and branding remain intact.
##
## Full license details available at http://www.jfxcms.com/license
##
###############   COPYLEFT GPLv3 LICENSE   ################



class JFX_Module_Jfxlinker extends JFX_Module {


    public $keyname = 'jfxlinker';
    public $title = 'JFX Linker';
    public $currentPageId = 0;

    public function __construct(){
        parent::__construct();
        // automatically load our configuration settings once only
        if(count($this->JFXLinker_Config) == 0) $this->LoadConfig();
        if(count($this->JFXLinker_Config) == 0){
            // probably not installed, can't proceed
            return;
        };
		$this->title = $this->lang('module_title');
    }




    public function mapUrl($url, $pageid, $moduleKey, $params){
        $DB = JFX::registry('db');
        $CONFIG = JFX::registry('config');
        if(!is_array($params)) $params = array();
        ksort($params);
        $dbParams = array(
            'url' => $url,
            'page_id' => $pageid,
            'module_key' => $moduleKey,
            'params' => serialize($params)
        );
        $safeUrl = $DB->escape($url);
        $found = $DB->fetchRow("SELECT * FROM {$CONFIG->dbprefix}jfxlinker_mapper WHERE url = '{$safeUrl}'");
        if(!is_array($found) || count($found)==0)
            $DB->insert($CONFIG->dbprefix.'jfxlinker_mapper', $dbParams);
        else
            $DB->update($CONFIG->dbprefix.'jfxlinker_mapper', $dbParams, "id = '{$found['id']}'");
    }

    public function removeUrl($url){
        $DB = JFX::registry('db');
        $CONFIG = JFX::registry('config');
        $url = $DB->escape($url);
        $DB->delete($CONFIG->dbprefix.'jfxlinker_mapper', "url = '{$url}'");
    }





    public function getUrl($pageid, $params=array()){
        $DB = JFX::registry('db');
        $CONFIG = JFX::registry('config');
        ksort($params);

        $eParams = array(
            'page_id' => $pageid,
            'params' => $params
        );
        JFX::hook('JFX_Link.getUrl', $eParams);
        
        $paramsSerialized = $DB->escape(serialize($params));
        $pageidEscaped = $DB->escape($pageid);
        $found = $DB->fetchRow("SELECT * FROM {$CONFIG->dbprefix}jfxlinker_mapper WHERE page_id = '{$pageidEscaped}' AND params = '{$paramsSerialized}'");
        if(!is_array($found) || count($found)==0){
            return $this->AddParamsToUrl($this->getUrlById($pageid), $params);
        }
    }



    public function getUrlById($pageid){
        $DB = JFX::registry('db');
        $CONFIG = JFX::registry('config');
        $pageid = (int) $pageid;
        $url = $CONFIG->baseUrl.'/'.$DB->oneValue($CONFIG->dbprefix.'pages', 'slug', "id = '{$pageid}'").$this->getConfig('ext');
        return $url;
    }



    public function makeLink($pageid, $blockid=NULL, $params=array(), $htmlentities=false){
        $CONFIG = JFX::registry('config');
        $url = $CONFIG->baseUrl.'/index.php';
        if($params == '') $params = array();
        $joiner = ($htmlentities) ? '&amp;' : '&';
        $params['page'] = $pageid;

        if(is_array($params) && count($params)>0){
            $newParams = array();
            foreach($params as $k=>$v){
                $newParams[] = urlencode($k).'='.urlencode($v);
            }
            $url .= '?'.implode($joiner, $newParams);
        }else if(is_string($params) && strlen($params)>0){
            $url .= (false===strpos($params, '?'))
                ?   '?'.$params
                :   $joiner.$params
                ;
        }

        if($blockid !== NULL){
            $url .= '#'.$blockid;
        }

        return $url;
    }





	/**
	 * Add parameters to a url
	 *
	 * @param string $url
	 * @param array $params
	 * @param boolean $htmlentities
	 * @return string url
	 *
	 */

    function AddParamsToUrl($url, $params, $htmlentities = false){
        if($htmlentities) $amp = '&amp;';
        else $amp = '&';

        if(false && (int) $this->getConfig('classic_urls')==1){
            $params2 = array();
            foreach($params as $k=>$v){
                $params2[urlencode($k)] = urlencode($v);
            };

            
            foreach($params2 as $k=>$v){
                $urls = explode('/', $url);
                $urlsRev = array_reverse($urls);
                if(trim($urlsRev[0])==''){
                    // the url has a trailing slash
                    $urlsRev = array_insert($urlsRev, 2, $v);
                    $urlsRev = array_insert($urlsRev, 3, $k);
                }else{
                    // the url has no trailing slash
                    $urlsRev = array_insert($urlsRev, 1, $v);
                    $urlsRev = array_insert($urlsRev, 2, $k);
                }
                $urls = array_reverse($urlsRev);
                $url = implode('/', $urls);
            };
            

        }else{
            // not using mod_rewrite
            foreach($params as $key=>$value){
                $url .= (false===strpos($url, '?'))
                    ?   '?'.urlencode($key).'='.urlencode($value)
                    :   $amp.urlencode($key).'='.urlencode($value)
                    ;
            };
        };

        return $url;
    }





    /**
     * Distributes url parameters formed by JFX::addParamsToUrl() into the $_GET array
     *
     * @param string $url
     */
    public function InterpretUrlParams($url=''){
        if($url == '') $url = THIS_PAGE_URL;
        $params = $this->getUrlParamsAsArray($url);
		foreach($params as $k=>$v){
			$_GET[$k] = $v;
            $_REQUEST[$k] = $v;
		}
    }


	/**
	 * Analyze a given url and return an array with parameters as key=>value pairs
	 *
	 * @param string $url
	 * @param string $mode 'auto', 'classic' or 'rewrite'
	 * @global object $gCms used to determine mode in case of auto
	 * @return array params
	 */

	public function getUrlParamsAsArray($url, $mode='auto'){
        $CONFIG = JFX::registry('config');
        $DB = JFX::registry('db');
        $params = array();
        
        // first up is our mapper
        $absUrl = str_replace($CONFIG->baseUrl, '', $url);
        $absUrl = '/'. trim($absUrl, '/');
        $safeUrl = $DB->escape($absUrl);

        $row = $DB->fetchRow("SELECT * FROM {$CONFIG->dbprefix}jfxlinker_mapper WHERE url = '{$safeUrl}'");

        if(is_array($row) && count($row)>0){
            // lets make this easy by rebuilding a classic url
            $url = $this->getUrlById($row['page_id']);
            $newParams = array();
            foreach(unserialize($row['params']) as $k=>$v){
                $newParams[] = $k.'='.$v;
            }
            $url .= (false===strpos($url, '?'))
                ?   '?'.implode('&', $newParams)
                :   '&'.implode('&', $newParams)
                ;

        }else{

            // now lets evaluate a dynamic rewritten url
    /*
                $urls = str_replace($CONFIG->baseUrl, '', $url);
                $urls = explode('/', trim($urls, '/'));
                $urls = array_reverse($urls);
                $value = '';

                for($i=1; $i<count($urls); $i++){
                    if($i%2==0){
                        // this is a key
                        $params[urldecode($urls[$i])] = urldecode($value);
                    }else{
                        // this is a value
                        $value = $urls[$i];
                    };
                };
*/
                

            
        }



        // url is now treated as a classic url

        // try to resolve classic url first
		
        $url = str_replace($CONFIG->baseUrl, '', $url);
        $url = '/'. trim($url, '/');
        $url = $DB->escape($url);
        
        if(strpos($url, '?')!==false){
            $parts = explode('?', $url);
            $urlparams = $parts[1];
        }else{
            // no querie parameters
            $urlparams = '';
        }

        $queries = explode('&', $urlparams);

        $retParams = array();

        foreach($queries as $k=>$v){
            if($v=='' || strstr($v, '=')===false) continue;
            $key = substr($v, 0, strpos($v, '='));
            $value = substr($v, strpos($v, '=')+1);
            $retParams[urldecode($key)] = urldecode($value);
        };

        //var_dump($retParams);

		return $retParams;

	}



    public function getPageId($url=''){

        // cached to reduce impact on db
        //if($this->currentPageId > 0) return $this->currentPageId;

        if($url == '') $url = THIS_PAGE_URL;

        // get some objects and variables
        global $pageid;

        

        $CONFIG = JFX::registry('JFXConfig');
        $USER = JFX::registry('JFX_User');
        $DB = JFX::registry('db');
        $params = $this->getUrlParamsAsArray($url);

        $domainid = $CONFIG->domainid;
        $workspaceid = (int) $USER->workspace;
        if($workspaceid == 0) $workspaceid = 1;
        $workspaceid = 1;

        // give other modules a chance to catch the page load
        $params = array('page_id' => 0);
        JFX::hook('JFX_Link.getPageId', $params);
        if((int) $params['page_id']>0){
            $tempPageId = (int) $params['page_id'];
            if($DB->countRows($CONFIG->dbprefix.'pages', "id = '{$tempPageId}' AND workspace_id = '{$workspaceid}' AND domain_id = '{$domainid}'")){
                $this->currentPageId = $tempPageId;
                return $tempPageId;
            }
        }

        // see if we have the page id in the url
        if((int) get('page', 0) > 0){
            $page = (int) get('page');
            if($DB->countRows($CONFIG->dbprefix.'pages', "id = '{$page}' AND workspace_id = '{$workspaceid}' AND domain_id = '{$domainid}'")){
                $this->currentPageId = $page;
                return $page;
            }
        }

        $request = str_replace($CONFIG->baseUrl, '', $url);
        $request = trim($request, '/');

        if($request == ''){
            // show the home page
            $pageid = (int) $DB->oneValue($CONFIG->dbprefix.'pages', 'id', "workspace_id = '{$workspaceid}' AND domain_id = '{$domainid}' AND is_default = 1");
            if($pageid > 0){
                $this->currentPageId = $pageid;

                return $pageid;
            }else{
                return $this->getConfig('404_page_id');
            }
        }


        // route url mapping
        $safeRequest = $DB->escape('/'.trim($request, '/'));
        $res = $DB->fetchRow("SELECT * FROM {$CONFIG->dbprefix}jfxlinker_mapper WHERE url = '{$safeRequest}'");
        
        if(is_array($res) && count($res)>0){
            $newUrl = $this->getUrlById($res['page_id']);
            $urlParams = unserialize($res['params']);
            if(is_array($urlParams)) $newUrl = $this->AddParamsToUrl($newUrl, $urlParams);
            $this->InterpretUrlParams($newUrl);
            $this->currentPageId = $res['page_id'];

            return $res['page_id'];
        }


        if(!isset($params['page'])){
            // page id not passed in ?page= params of url, assess rewritten urls
            $urlParts = explode('?', $_SERVER['REQUEST_URI']);
            
            $urlParts = explode('/', $urlParts[0]);
            $lastPart = $urlParts[count($urlParts)-1];
            $lastPart = explode('.', $lastPart);
            $slug = $DB->escape($lastPart[0]);
            if($DB->countRows($CONFIG->dbprefix.'pages', "slug = '{$slug}' AND workspace_id = '{$workspaceid}' AND domain_id = '{$domainid}' AND is_published = 1")>0){
                 // found a page by the slug in the filename of the url
                 $pageid = $DB->oneValue($CONFIG->dbprefix.'pages', 'id', "slug = '{$slug}' AND workspace_id = '{$workspaceid}' AND domain_id = '{$domainid}' AND is_published = 1");
            }else{
                // 404
                
                return $this->getConfig('404_page_id');
            }
            
        }else{
            if($params['page'] === intval($params['page'])){
                if($DB->countRows($CONFIG->dbprefix.'pages', "id = '{$pageid}' AND workspace_id = '{$workspaceid}' AND domain_id = '{$domainid}' AND is_published = 1")>0)
                    // found by ?page=(int)$id param in url
                    $pageid = $params['page'];
            }else{
                $slug = $DB->escape($params['page']);
                if($DB->countRows($CONFIG->dbprefix.'pages', "slug = '{$slug}' AND workspace_id = '{$workspaceid}' AND domain_id = '{$domainid}' AND is_published = 1")>0){
                    // found by ?page=$slug param in url
                    $pageid = $DB->oneValue($CONFIG->dbprefix.'pages', 'id', "slug = '{$params['page']}' AND workspace_id = '{$workspaceid}' AND domain_id = '{$domainid}' AND is_published = 1");
                }else {
                    // 404

                    return $this->getConfig('404_page_id');
                }
            }
        }
        
        $this->currentPageId = $pageid;
        return $pageid;

    }



    public function makeUniqueAlias($preferredAlias){
        $CONFIG = JFX::registry('config');
        $alias = '/'.trim($preferredAlias, '/');
        $_404pageid = $this->getConfig('404_page_id');
        $attempts = 0;
        while($this->getPageId($CONFIG->baseUrl.$alias) != $_404pageid && $attempts < 50){
            $attempts++;
            // increment our alias
            $lastLetter = substr($alias, strlen($alias)-1);
            if(intval($lastLetter)!=$lastLetter){
                $alias = $alias.'0';
                $lastLetter = 0;
            }
            $newLastLetter = $lastLetter+1;
            if($newLastLetter == 0){
                $alias = substr($alias, 0, strlen($alias)-1).$lastLetter.'0';
            }else{
                $alias = substr($alias, 0, strlen($alias)-1).$newLastLetter;
            }
        }
        return $alias;
    }





    public function throw404(){
        global $pageLoadStartTime;
        $CONFIG = JFX::registry('JFXConfig');
        $USER = JFX::registry('JFX_User');
        $DB = JFX::registry('db');

        $request = str_replace($CONFIG->prefix, '', $_SERVER['REQUEST_URI']);
        $request = trim($request, '/');
        $safeRequest = $DB->escape('/'.$request);


        // store 404's for future remapping
        $res = $DB->fetchRow("SELECT * FROM {$CONFIG->dbprefix}jfxlinker_404s WHERE url = '{$safeRequest}'");
        if(is_array($res) && count($res)>0){
            // update previous 404
            $res['num_requests']++;
            $res['last_request_tstamp'] = time();
            $DB->update($CONFIG->dbprefix.'jfxlinker_404s', $res, "id = '{$res['id']}'");
        }else{
            // insert new 404
            $dbParams = array(
                'url' => '/'.$request,
                'num_requests' => 1,
                'first_request_tstamp' => time(),
                'last_request_tstamp' => time()
            );
            $DB->insert($CONFIG->dbprefix.'jfxlinker_404s', $dbParams);
        }

        $LANG = JFX::registry('lang');
        $VIEW = JFX::registry('JFX_View');
        $CONFIG = JFX::registry('config');
        $USER = JFX::registry('JFX_User');
        $SMARTY = JFX::registry('Smarty');
        $DB = JFX::registry('db');

        header("HTTP/1.0 404 Not Found");

        // start a new page
        $VIEW = new JFX_View;
        JFX::register('JFX_View', $VIEW);

        $VIEW->template = 'cms-default.tpl';

        $pageid = $this->getConfig('404_page_id');

        $VIEW->setTitle($LANG->getContent('core', 'heading', $pageid));

        $content = JFX::getPageContent($pageid, 1);

        echo $content;

        require($CONFIG->baseDir.'/jfx-system-end.php');
        exit;

    }






    public function admin($url, $dirname){

        // get hold of our objects
        $DB = JFX::registry('db');
        $SMARTY = JFX::registry('Smarty');
        $USER = JFX::registry('JFX_User');
        $CRYPT = JFX::registry('JFX_Crypt');
        $CONFIG = JFX::registry('config');
        $VIEW = JFX::registry('JFX_View');
        $LANG = JFX::registry('lang');

        $this->addStylesheet('backend.css');

        $request = trim(str_replace($url, '', THIS_PAGE_URL), '/');
        $request = str_replace('../', '', $request);
        $request = explode('?', $request);
        $request = explode('/', trim($request[0], '/'));
        $activeTab = array_shift($request);

        $tabs = array();

        if($this->checkPermission('perm_list')){
            $tabs['list'] = $this->lang('list_tab');
        }
        
        if($this->checkPermission('perm_new')){
            $tabs['new'] = $this->lang('new_tab');
        }

        if($this->checkPermission('perm_new')){
            $tabs['edit'] = $this->lang('edit_tab');
        }

        if($this->checkPermission('perm_list')){
            $tabs['404'] = $this->lang('404_tab');
        }

        if($this->checkPermission('perm_config')){
            $tabs['config'] = $this->lang('config_tab');
        };


        if(file_exists($dirname.'/tabs/admin.'.$activeTab.'.php') && $this->checkPermission('perm_'.$activeTab)) $selectedTab = $activeTab;
        else $selectedTab = 'list';

        $actionUrls = array();

        // calculate the urls to get to each tab
        foreach($tabs as $tab=>$title){
            $params['active_tab'] = $tab;
            $actionUrls[$tab] = $url.'/'.$tab;
        };

        $SMARTY->assign('actionUrls', $actionUrls);

        $adminTabs = array();
        foreach($tabs as $k=>$title){
            if($k==$selectedTab) $class = 'selected';
            else $class = '';
            $adminTabs[] = array(
                'key'=>$k,
                'title' => $title,
                'url' => $actionUrls[$k],
                'class' => $class
            );
        };
        $SMARTY->assign('adminTabs', $adminTabs);

        echo $this->fetchTemplate('admin.tabs.tpl');

        echo '<div id="admin-content">';
        include($dirname.'/tabs/admin.'.$selectedTab.'.php');
        echo '</div>';






    }

    /**
     * The pop-up window in TinyMCE allowing the user to choose a page to link to
     */
    public function pagePicker(){

        $VIEW = JFX::registry('JFX_View');
        $CONFIG = JFX::registry('config');

        $VIEW->template = 'tinymce-jfxlinker.tpl';
        $VIEW->addCssLink('admin.css');
        $VIEW->addCssLink('forms.css');
        $VIEW->addCssLink('jquery.treeview.css');
        $VIEW->addJsLink($CONFIG->jsUrl.'/tiny_mce/tiny_mce_popup.js');
        $VIEW->addJsLink($CONFIG->jsUrl.'/tiny_mce/plugins/jfxlinker/js/dialog.js');


        $VIEW->addJs('
        $(document).ready(function(){
            $("#page-tree").treeview({
                persist: "location",
                collapsed: true,
                unique: true
            });
        });

        function JFXLinkerDialogAddLink(url, title, target){
                var ed = tinyMCEPopup.editor, e, b, target = "_self", title = "";

                //var title = ($("#jfxlinker-title").get(0).value == "") ? null : $("#jfxlinker-title").get(0).value;

                tinyMCEPopup.restoreSelection();
                e = ed.dom.getParent(ed.selection.getNode(), "A");

		tinyMCEPopup.execCommand("mceBeginUndoLevel");

		// Create new anchor elements
		if (e == null) {
			tinyMCEPopup.execCommand("CreateLink", false, "#mce_temp_url#", {skip_undo : 1});

			tinymce.each(ed.dom.select("a"), function(n) {
				if (ed.dom.getAttrib(n, "href") == "#mce_temp_url#") {
					e = n;

					ed.dom.setAttribs(e, {
						href : url,
						title : title,
						target : target
					});
				}
			});
		} else {
			ed.dom.setAttribs(e, {
				href : url,
				title : title,
				target : target
			});
		}

		// Don\'t move caret if selection was image
		if (e && e.childNodes.length != 1 || e.firstChild.nodeName != "IMG") {
			ed.focus();
			ed.selection.select(e);
			ed.selection.collapse(0);
			tinyMCEPopup.storeSelection();
		}

		tinyMCEPopup.execCommand("mceEndUndoLevel");
		tinyMCEPopup.close();


        }



        ');

        $pages = $this->getPages();
        echo '<ul id="page-tree">'.$this->printPageLinks($pages).'</ul>';



    }




    function parseLinks($content){
        $CONFIG = JFX::registry('config');
        //return $content;
        

        $dom = phpQuery::newDocumentXhtml($content);

        $scripts = pq('script');
        foreach($scripts as $k=>$script){
            if(strlen($script->nodeValue)==0) $scripts[$k]->nodeValue = ' ';
        };

        $divs = pq('div');
        foreach($divs as $k=>$div){
            if($div->nodeValue == '' && count($div->childNodes)==0) $divs[$k]->nodeValue = ' ';
        };

        $as = pq('a');
        foreach($as as $k=>$a){
            if(strlen($a->nodeValue) == 0 && count($a->childNodes)==0) $as[$k]->nodeValue = ' ';
        };

        // we must fix script links
        $scripts = pq('script');
        foreach($scripts as $k=>$script){
            $url = $script->getAttribute('src');
            if(strlen($url)>0 && strpos($url, 'http')!==0 && strpos($url, '/')!==0){
                // the url is relative, lets make it absolute
                $url = $this->psUrl.'/'.$url;
                //$scripts[$k]->setAttribute('src', $url);
            }
        }

        // here, we do the heavy lifting of converting stored urls to real urls
        $as = pq('a');
        foreach($as as $k=>$a){
            // lets fix the hrefs
            $url = $a->getAttribute('href');
            if(substr($url, 0, 10)==='jfxlinker:'){
                // got a url to parse
                $urlParts = explode(':', $url);
                $pageid = $urlParts[1];
                $url = $this->getUrlById($pageid);
                $a->setAttribute('href', $url);
            }
        }

        $content = (string) $dom;
        unset($dom, $scsripts, $as);
        return $content;

    }




    function printPageLinks($pages){
        $LINK = JFX::registry('JFX_Link');
        $returnString = '';
        foreach($pages as $k=>$v){
            $returnString .= '<li><a href="javascript: void(null);" onclick="JFXLinkerDialogAddLink(\'jfxlinker:'.$v['id'].'\', \''.$v['title'].'\')">'.$v['title'].'</a>';
            if(count($v['subpages'])>0){
                $returnString .= '<ul>'.$this->printPageLinks($v['subpages']).'</ul>';
            }
            $returnString .= '</li>';
        }
        return $returnString;
    }



    function getPages($parentid=0){
        $DB = JFX::registry('db');
        $LANG = JFX::registry('lang');
        $CONFIG = JFX::registry('config');
        $parentid = (int) $parentid;
        $subpages = $DB->fetchAll("SELECT * FROM {$CONFIG->dbprefix}pages WHERE parent_id = {$parentid} ORDER BY sorting ASC");
        if(!is_array($subpages)) $subpages = array();
        foreach($subpages as $k=>$v){
            $subpages[$k]['title'] = $LANG->getContent('core', 'heading', $v['id']);
            $subpages[$k]['subpages'] = $this->getPages($v['id']);
        };

        return $subpages;
    }





    function LoadConfig(){
        $DB = JFX::registry('db');
        $CONFIG = JFX::registry('config');
        // make sure we have been installed!

        if(!JFX_Module::isInstalled($this->keyname)) return;

        $config = $DB->fetchAll("SELECT * FROM {$CONFIG->dbprefix}jfxlinker_config");

        if($config === false) return;
        foreach($config as $k=>$v){
            $this->JFXLinker_Config[$v['k']] = $v['v'];
        };
    }




    function GetConfig($k){
        if(isset($this->JFXLinker_Config[$k])) return $this->JFXLinker_Config[$k];
        else if($this->ThrowConfigExceptions) throw new Exception($this->getTitle().'->GetConfig was asked to find a non-existing key: '.$k);
        else return false;
    }




   public function install(){
        $VIEW = JFX::registry('JFX_View');
        $SMARTY = JFX::registry('Smarty');
        $USER = JFX::registry('JFX_User');
        $CONFIG = JFX::registry('config');
        $DB = JFX::registry('db');
        $dirname = str_replace(DIRECTORY_SEPARATOR, '/', dirname(__FILE__)).'/install';
        include('install/install.php');
    }


    public function uninstall(){
        $VIEW = JFX::registry('JFX_View');
        $SMARTY = JFX::registry('Smarty');
        $USER = JFX::registry('JFX_User');
        $CONFIG = JFX::registry('config');
        $DB = JFX::registry('db');
        $dirname = str_replace(DIRECTORY_SEPARATOR, '/', dirname(__FILE__)).'/install';
        include('install/uninstall.php');
    }



}